package net.cyndeline.scalarlib.rlgraph.cycles

import net.cyndeline.scalarlib.rlgraph.util.GraphCommons

/**
 * A cycle in a graph. The last element is connected to the first.
 */
class Cycle[V] private (val vertices: Vector[V]) {

  val length: Int = vertices.size

  val isSimple: Boolean = vertices.distinct.size == length

  val edges: Vector[(V, V)] = makeEdges()

  val edgeSize: Int = edges.size

  def reverse: Cycle[V] = Cycle(vertices.reverse)

  override def equals(other: Any): Boolean = other match {
    case c: Cycle[V] => {
      if (c.edgeSize != edgeSize || c.length != length) return false
      else if (this eq c) return true
      else if (this.## != c.##) return false

      val otherEdges = c.edges
      val startEdge = otherEdges.head
      val localStartEdge = edges.find(_ == startEdge).getOrElse(return false)
      val localEdgesWithStartFirst = GraphCommons.placeElementFirst(localStartEdge, edges)
      localEdgesWithStartFirst == otherEdges
    }
    case _ => false
  }

  override def hashCode: Int = vertices.map(_.##).sum

  override val toString: String = "Cycle: " + vertices.mkString(", ")

  private def makeEdges(): Vector[(V, V)] = {
    val listWithLast = vertices :+ vertices.head
    listWithLast zip listWithLast.tail
  }
}

object Cycle {
  def apply[V](vertices: Vector[V]) = new Cycle(vertices)
  def apply[V](v: V, vs: V*) = new Cycle(Vector(v) ++ vs.toVector)
}
